package com.tiancheng.trade.order.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.enums.PaySceneEnum;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.enums.PaymentWayEnum;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.web.model.request.CloseOrderDTO;
import com.tiancheng.trade.commom.web.model.request.IntRefundDTO;
import com.tiancheng.trade.commom.web.model.request.PaypalCreateDTO;
import com.tiancheng.trade.commom.web.model.request.PaypalExecuteDTO;
import com.tiancheng.trade.commom.web.model.response.UnifiedOrderResVO;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.order.util.SecurityUtils;
import com.tiancheng.trade.order.bo.*;
import com.tiancheng.trade.order.enums.*;
import com.tiancheng.trade.commom.web.feign.PaymentFeignService;
import com.tiancheng.trade.order.mapper.IntOrderMapper;
import com.tiancheng.trade.order.mapper.IntRefundMapper;
import com.tiancheng.trade.order.model.*;
import com.tiancheng.trade.order.model.join.IntOrderJoin;
import com.tiancheng.trade.order.service.*;
import com.tiancheng.trade.order.util.*;
import com.tiancheng.trade.order.util.cache.OrderCache;
import com.tiancheng.trade.order.mq.MessageProducer;
import com.tiancheng.trade.order.util.helper.OrderIdHelper;
import com.tiancheng.trade.order.util.lock.Lock;
import com.tiancheng.trade.order.vo.admin.QueryOrdersVO;
import com.tiancheng.trade.order.vo.admin.res.*;
import com.tiancheng.trade.order.vo.api.CloseOrderVO;
import com.tiancheng.trade.order.vo.api.RefundVO;
import com.tiancheng.trade.order.vo.api.res.CreateOrderResVO;
import com.tiancheng.trade.order.vo.api.res.PaypalQueryResVO;
import com.tiancheng.trade.order.vo.api.res.RefundCreateResVO;
import com.tiancheng.trade.order.vo.app.*;
import com.tiancheng.trade.order.vo.app.res.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目名称：trade-order
 * 类 名 称：IntOrderService
 * 类 描 述：英文版主订单操作
 */
@Slf4j
@Service
public class IntOrderServiceImpl implements IntOrderService {

    @Autowired
    private IntOrderMapper intOrderMapper;
    @Autowired
    private IntRefundMapper intRefundMapper;
    @Autowired
    private IntSubOrderService intSubOrderService;
    @Resource
    private PaymentFeignService paymentFeignService;
    @Resource
    private OrderIdHelper orderIdHelper;
    @Resource
    private RefundOrdersDetailService refundOrdersDetailService;

    @Value("${order.mainMerchant.code}")
    private String mainMerchantCode;
    @Value("${order.mainMerchant.name}")
    private String mainMerchantName;

    private static final String CURRENCY = "USD";

    @Resource
    private MessageProducer messageProducer;

    @Override
    @Transactional
    public String create(OrderBO orderBO, List<SubOrderBO> subOrderBOS) throws BusinessException {
        // 重复下单判断
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderBO.getOrderId());
        conditions.eq("uid", orderBO.getUid());
        BusinessErrorInfoEnum.ORDER_CREATE_REPEAT.assertIsTrue(intOrderMapper.selectCount(conditions) == 0);

        // 如果是续费订单、尾款订单，验证关联的上一笔订单有效性
        OrderTypeEnum orderTypeEnum = EnumUtil.getEnumByCode(OrderTypeEnum.class, orderBO.getOrderType());
        if (orderTypeEnum == OrderTypeEnum.renew || orderTypeEnum == OrderTypeEnum.balance) {
            QueryWrapper<IntOrder> originalConditions = new QueryWrapper<>();
            originalConditions.eq("order_id", orderBO.getOriginalOrderId());
            originalConditions.eq("order_status", OrderStatusEnum.paid.getCode());
            Long count = intOrderMapper.selectCount(originalConditions);
            BusinessErrorInfoEnum.ORDER_CREATE_ORIGINAL.assertIsTrue(count > 0);
        }

        // 保存主订单
        IntOrder intOrder = ObjectConvertUtil.convert(orderBO, IntOrder.class).get();
        intOrder.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
        intOrder.setPayStatus(OrderPayStatusEnum.un_pay.getCode());
        intOrder.setSubOrderCnt(subOrderBOS.size());
        intOrder.setCreatedDt(orderBO.getCreateTime());
        intOrder.setRefundTimes(0);        //初始退款次数为0
        // 支付过期时间，下单时间20分钟后
        if (intOrder.getPayExpiryTime() == null) {
            intOrder.setPayExpiryTime(intOrder.getCreatedDt().plusMinutes(20));
        }
        intOrder.setPayAmount(intOrder.getOrderAmount());
        intOrder.setCurrency(CURRENCY);//英文版默认为美金

        // 设置订单主商户，首先以业务侧传的为准
        if (StringUtils.isEmpty(intOrder.getMerchantCode())) {
            // 如果子订单有多个不同商户，默认使用腾云商户，否则主商户以子订单商户为准
            long subCounts = subOrderBOS.stream().map(SubOrderBO::getMerchantCode).distinct().count();
            if (subCounts == 1) {
                intOrder.setMerchantCode(subOrderBOS.get(0).getMerchantCode());
                intOrder.setMerchantName(subOrderBOS.get(0).getMerchantName());
            } else {
                intOrder.setMerchantCode(mainMerchantCode);
                intOrder.setMerchantName(mainMerchantName);
            }
        }
        int num = intOrderMapper.insert(intOrder);

        // 保存子订单
        intSubOrderService.create(subOrderBOS);

        return intOrder.getOrderId();
    }

    @Override
    @Transactional
    public CreateOrderResVO createOrder(OrderBO orderBO, List<SubOrderBO> subOrderBOS) throws BusinessException {
        // 重复下单判断
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderBO.getOrderId());
        conditions.eq("uid", orderBO.getUid());
        BusinessErrorInfoEnum.ORDER_CREATE_REPEAT.assertIsTrue(intOrderMapper.selectCount(conditions) == 0);

        // 如果是续费订单、尾款订单，验证关联的上一笔订单有效性
        OrderTypeEnum orderTypeEnum = EnumUtil.getEnumByCode(OrderTypeEnum.class, orderBO.getOrderType());
        if (orderTypeEnum == OrderTypeEnum.renew || orderTypeEnum == OrderTypeEnum.balance) {
            QueryWrapper<IntOrder> originalConditions = new QueryWrapper<>();
            originalConditions.eq("order_id", orderBO.getOriginalOrderId());
            originalConditions.eq("order_status", OrderStatusEnum.paid.getCode());
            Long count = intOrderMapper.selectCount(originalConditions);
            BusinessErrorInfoEnum.ORDER_CREATE_ORIGINAL.assertIsTrue(count > 0);
        }

        // 保存主订单
        IntOrder intOrder = ObjectConvertUtil.convert(orderBO, IntOrder.class).get();
        intOrder.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
        intOrder.setPayStatus(OrderPayStatusEnum.un_pay.getCode());
        intOrder.setSubOrderCnt(subOrderBOS.size());
        intOrder.setCreatedDt(orderBO.getCreateTime());
        intOrder.setRefundTimes(0);        //初始退款次数为0
        // 支付过期时间，下单时间20分钟后
        if (intOrder.getPayExpiryTime() == null) {
            intOrder.setPayExpiryTime(intOrder.getCreatedDt().plusMinutes(20));
        }
        intOrder.setPayAmount(intOrder.getOrderAmount());
        intOrder.setCurrency(CURRENCY);//英文版默认为美金

        // 设置订单主商户，首先以业务侧传的为准
        if (StringUtils.isEmpty(intOrder.getMerchantCode())) {
            // 如果子订单有多个不同商户，默认使用腾云商户，否则主商户以子订单商户为准
            long subCounts = subOrderBOS.stream().map(SubOrderBO::getMerchantCode).distinct().count();
            if (subCounts == 1) {
                intOrder.setMerchantCode(subOrderBOS.get(0).getMerchantCode());
                intOrder.setMerchantName(subOrderBOS.get(0).getMerchantName());
            } else {
                intOrder.setMerchantCode(mainMerchantCode);
                intOrder.setMerchantName(mainMerchantName);
            }
        }
        int num = intOrderMapper.insert(intOrder);

        // 保存子订单
        intSubOrderService.create(subOrderBOS);

        CreateOrderResVO createOrderResVO=new CreateOrderResVO();
        createOrderResVO=ObjectConvertUtil.convert(intOrder, CreateOrderResVO.class).get();

        List<SubOrder> subOrders=subOrderBOS.stream().map(subOrderBO -> {
            SubOrder subOrder = ObjectConvertUtil.convert(subOrderBO, SubOrder.class).get();
            subOrder.setSubOrderStatus(OrderStatusEnum.waiting_pay.getCode());
            subOrder.setPayAmount(subOrder.getSubOrderAmount()-subOrder.getDiscount());
            //subOrder.setCurrency(CURRENCY);
            subOrder.setCreatedDt(subOrderBO.getCreateTime());
            subOrder.setRefundAmount(0);
            return subOrder;
        }).collect(Collectors.toList());
        createOrderResVO.setSubOrders(subOrders);
        return createOrderResVO;
    }

    @Override
    public boolean closeOrder(CloseOrderVO closeOrderVO) throws BusinessException {
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("order_id", closeOrderVO.getOrderId());
        conditions.eq("uid", closeOrderVO.getUid());
        IntOrder intOrder = intOrderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.ORDER_CLOSE_NO_DATA.assertNotNull(intOrder);
        BusinessErrorInfoEnum.ORDER_CLOSE_STATUS_ERROR.assertIsTrue(OrderStatusEnum.waiting_pay.getCode().equals(intOrder.getOrderStatus()));
        BusinessErrorInfoEnum.ORDER_CLOSE_REFUSE.assertIsFalse(OrderPayStatusEnum.paying.getCode().equals(intOrder.getPayStatus()));

        if (OrderStatusEnum.cancelled.getCode().equals(intOrder.getOrderStatus())) {
            return true;
        }
        if (StringUtils.isEmpty(intOrder.getOrderId())) {
            intOrder.setOrderId(closeOrderVO.getOrderId());
        }
        // 关闭相应的流水
        this.closeTradeFlow(intOrder);

        IntOrder updateOrder = new IntOrder();
        updateOrder.setOrderStatus(OrderStatusEnum.cancelled.getCode());
        intOrderMapper.update(updateOrder, conditions);
        return true;
    }


    /**
     * 关闭订单、修改订单金额时，需要关闭相应的流水
     *
     * @param intOrder
     */
    private void closeTradeFlow(IntOrder intOrder) {
            // 如果创建paypal支付订单后，又取消，此时支付信息在缓存中，所以要先查询一下缓存，决定是否要通知支付中心关闭
            List<String> codeList = PaySceneEnum.getCodeList();
            codeList.add(null);
            for (String s : codeList) {
                String cacheUniqueKey = orderIdHelper.getUniqueId(intOrder.getUid(), intOrder.getOrderId(),
                        intOrder.getPayWay()==null? PaymentWayEnum.PAYPAL.getCode():intOrder.getPayWay(),
                        s+"PrePay");//创建paypal支付订单时产生流水。创建成功后缓存支付信息，用payScene+"PrePay"区分确认支付接口返回的实际支付信息
                Optional<UnifiedOrderResVO> prePayInfo = OrderCache.getPrePayInfo(cacheUniqueKey);
                if (prePayInfo.isPresent()) {
                    String flowId = String.valueOf(prePayInfo.get().getFlowId());
                    log.info("关闭订单流水,uid:{},orderId:{},flowId:{},cacheUniqueKey:{}", intOrder.getUid(), intOrder.getOrderId(), flowId, cacheUniqueKey);
                    CloseOrderDTO closeOrderDTO = new CloseOrderDTO();
                    closeOrderDTO.setUid(intOrder.getUid());
                    closeOrderDTO.setFlowId(flowId);
                    Result result = paymentFeignService.closeIntOrder(closeOrderDTO);
                    log.info("关闭订单流水结果：{}", result);

                    OrderCache.delPrePayInfo(cacheUniqueKey);
                }
            }

    }


    /**
     * paypal支付订单创建
     * @param uid
     * @param reqVO
     * @return
     * @throws BusinessException
     */
    @Override
    @Transactional
    public PaypalCreateResVO createPaypal(String uid, PaypalCreateReqVO reqVO) throws BusinessException {

        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.in("order_id", reqVO.getOrderId());
        conditions.eq("order_status", OrderStatusEnum.waiting_pay.getCode());
        conditions.eq("pay_status", OrderPayStatusEnum.un_pay.getCode());
        List<IntOrder> orders = intOrderMapper.selectList(conditions);

        // 检查订单状态、订单支付失效时间、订单禁止跨渠道支付
        BusinessErrorInfoEnum.EN_ORDER_PAY_STATUS.assertIsTrue(orders.size() == 1);
        String payChannel = orders.get(0).getPayChannel();
        for (IntOrder order : orders) {
            BusinessErrorInfoEnum.EN_ORDER_PAY_CHANNEL.assertIsTrue(payChannel.equals(order.getPayChannel()));
            BusinessErrorInfoEnum.EN_ORDER_PAY_TIMEOUT_CLOSE.assertIsTrue(order.getPayExpiryTime().isAfter(LocalDateTime.now()));
        }

        // 组织返回的支付信息
        PaypalCreateResVO paypalCreateResVO = new PaypalCreateResVO();

        LocalDateTime payExpiryTime = orders.stream().map(IntOrder::getPayExpiryTime).min(
                Comparator.comparing(date -> date.toEpochSecond(ZoneOffset.of("+8")))).get();
        paypalCreateResVO.setPayLeftTime(payExpiryTime.toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));


        // 针对该用户加锁，5秒后自动释放，并发等待5秒获取，5秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_ORDER_CREATE_PAY, uid, 5, 5)) {
            BusinessErrorInfoEnum.EN_ORDER_PAY_TIMEOUT.assertIsTrue(lock.getLockRes());

            // 判断并返回缓存中的创建支付信息,缓存时长为半小时
            Optional<UnifiedOrderResVO> createPayInfo = this.getCachePrePayInfo(uid, reqVO, orders);
            if (createPayInfo.isPresent()) {
                JSONObject jsonObject= JSON.parseObject(JsonUtil.toJsonString(createPayInfo));
                paypalCreateResVO= jsonObject.toJavaObject(PaypalCreateResVO.class);
                return paypalCreateResVO;
            }

            String orderSource = orders.size() > 1 ? OrderSourceEnum.CONSOLIDATION.getCode() : orders.get(0).getOrderSource();
            String paymentId = orderIdHelper.getPaymentId(uid);

            PaypalCreateDTO paypalCreateDTO=new PaypalCreateDTO();
            paypalCreateDTO.setUid(uid);
            paypalCreateDTO.setOrderName(orders.get(0).getOrderName());
            paypalCreateDTO.setUserTel(orders.get(0).getMobile());
            paypalCreateDTO.setPaymentId(paymentId);
            paypalCreateDTO.setBusinessType(orderSource);
            paypalCreateDTO.setTs(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            paypalCreateDTO.setCurrency(orders.get(0).getCurrency());
            paypalCreateDTO.setDescription(reqVO.getDescription());

            //业务系统创建订单时传入的支付金额为int型，美分，存入订单表和传入保持一致
            int amountFen=orders.stream().mapToInt(IntOrder::getPayAmount).sum(); //美分
//            BigDecimal amountYuan=new BigDecimal(amountFen)
//                    .divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
            int orderAmountFen=orders.stream().mapToInt(IntOrder::getOrderAmount).sum(); //美分
//            BigDecimal orderAmountYuan=new BigDecimal(orderAmountFen)
//                    .divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);

            paypalCreateDTO.setAmount(amountFen);
            paypalCreateDTO.setOrderAmount(orderAmountFen);
            paypalCreateDTO.setPayScene(reqVO.getPayScene());
            paypalCreateDTO.setPayChannel(payChannel);
            paypalCreateDTO.setPayWay(reqVO.getPayWay());
            paypalCreateDTO.setOpenid(reqVO.getOpenid());
            paypalCreateDTO.setSubAppId(reqVO.getSubAppId());
            paypalCreateDTO.setSuccessUrl(reqVO.getSuccessUrl());
            paypalCreateDTO.setCancelUrl(reqVO.getCancelUrl());
            //paypalCreateDTO.setFailUrl(reqVO.getFailUrl());
            // 如果是小程序支付，没有传openid，默认使用创建订单时的openid
            if (OrderSceneEnum.miniapp.name().equals(reqVO.getPayScene()) && StringUtils.isEmpty(reqVO.getOpenid())) {
                paypalCreateDTO.setOpenid(orders.get(0).getOpenid());
            }
            // 设置主商户
            long mainCount = orders.stream().map(IntOrder::getMerchantCode).distinct().count();
            if (mainCount == 1) {
                paypalCreateDTO.setMerchantCode(orders.get(0).getMerchantCode());
                paypalCreateDTO.setMerchantName(orders.get(0).getMerchantName());
            } else {
                paypalCreateDTO.setMerchantCode(mainMerchantCode);
                paypalCreateDTO.setMerchantName(mainMerchantName);
            }

            // 组织子订单数据
            List<IntSubOrder> subOrders = intSubOrderService.find(reqVO.getOrderId());
            List<PaypalCreateDTO.SubOrderPaypalDTO> subOrderPaypalDTOS = subOrders.stream().map(intSubOrder -> {
                PaypalCreateDTO.SubOrderPaypalDTO subOrderPayDTO = ObjectConvertUtil.convert(intSubOrder, PaypalCreateDTO.SubOrderPaypalDTO.class).get();
                //子订单表的金额存的是int型美分。
                subOrderPayDTO.setOrderAmount(intSubOrder.getSubOrderAmount());
                subOrderPayDTO.setAmount(intSubOrder.getPayAmount());
                return subOrderPayDTO;
            }).collect(Collectors.toList());
            paypalCreateDTO.setSubPaypalOrders(subOrderPaypalDTOS);

            // 调用支付中心paypal支付服务
            Result<UnifiedOrderResVO> result = paymentFeignService.paypalCreate(paypalCreateDTO);
            log.info("支付单号:{},支付返回信息:{}", paymentId, result);
            BusinessErrorInfoEnum.EN_ORDER_PAY_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0);

            // 缓存创建支付信息
            String cacheUniqueKey = orderIdHelper.getUniqueId(uid, reqVO.getOrderId(),
                    reqVO.getPayWay(), reqVO.getPayScene()+"PrePay");
            log.info("支付订单，获取cacheUniqueKey：{}", cacheUniqueKey);
            OrderCache.prePayInfo(cacheUniqueKey, result.getData());

            // 缓存提交的支付参数
            PaypalParamsCacheBO paypalParamsCacheBO = ObjectConvertUtil.convert(reqVO, PaypalParamsCacheBO.class).get();
            paypalParamsCacheBO.setUid(uid);
            OrderCache.paypalParams(paymentId, paypalParamsCacheBO);

            JSONObject jsonObject= JSON.parseObject(JsonUtil.toJsonString(result.getData()));
            paypalCreateResVO= jsonObject.toJavaObject(PaypalCreateResVO.class);

            //创建完支付订单后将支付中心的支付订单号和支付流水号存入订单表
            QueryWrapper<IntOrder> updateCondition = new QueryWrapper<>();
            updateCondition.eq("order_id", reqVO.getOrderId());

            IntOrder updateOrder = new IntOrder();
            updateOrder.setPaymentId(paypalCreateResVO.getPaymentId());
            updateOrder.setFlowId(paypalCreateResVO.getFlowId());
            updateOrder.setPayWay(reqVO.getPayWay());
            updateOrder.setPayScene(reqVO.getPayScene());
            intOrderMapper.update(updateOrder, updateCondition);

            return paypalCreateResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.EN_ORDER_PAY_TIMEOUT, null, e.getMessage(), e);
        }
    }

    /**
     * 获取缓存中的创建支付信息
     * @param reqVO
     * @param orders
     * @return
     */
    private Optional<UnifiedOrderResVO> getCachePrePayInfo(String uid, PaypalCreateReqVO reqVO, List<IntOrder> orders) {
        String cacheUniqueKey = orderIdHelper.getUniqueId(uid, orders.get(0).getOrderId(),
                reqVO.getPayWay(), reqVO.getPayScene()+"PrePay");
        // 判断订单是否重复支付，直接从缓存拿预支付信息
        Optional<UnifiedOrderResVO> prePayInfo = OrderCache.getPrePayInfo(cacheUniqueKey);
        if (prePayInfo.isPresent()) {
            //return Optional.of(String.valueOf(prePayInfo.get().get("create_pay_info")));
            return prePayInfo;
        }
        return Optional.empty();
    }


    @Override
    public PaypalExecuteResVO executePaypay(String uid, PaypalExecuteReqVO reqVO) throws BusinessException {
        PaypalExecuteResVO paypalExecuteResVO=new PaypalExecuteResVO();

        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.in("order_id", reqVO.getOrderId());
        conditions.eq("order_status", OrderStatusEnum.waiting_pay.getCode());
        conditions.eq("pay_status", OrderPayStatusEnum.un_pay.getCode());
        List<IntOrder> orders = intOrderMapper.selectList(conditions);

        // 检查订单状态、订单支付失效时间、订单禁止跨渠道支付
        BusinessErrorInfoEnum.EN_ORDER_PAY_STATUS.assertIsTrue(orders.size() == 1);
        String payChannel = orders.get(0).getPayChannel();
        String paymentId=orders.get(0).getPaymentId();
        reqVO.setPayWay(orders.get(0).getPayWay());
        reqVO.setPayScene(orders.get(0).getPayScene());
        reqVO.setPayChannel(orders.get(0).getPayChannel());
        for (IntOrder order : orders) {
            BusinessErrorInfoEnum.EN_ORDER_PAY_CHANNEL.assertIsTrue(payChannel.equals(order.getPayChannel()));
            //BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT_CLOSE.assertIsTrue(order.getPayExpiryTime().isAfter(LocalDateTime.now()));
        }

        /*List<PaypalExecuteResVO.Order>  orderList=new ArrayList<>();
        PaypalExecuteResVO.Order order=new PaypalExecuteResVO.Order();
        order.setOrderAmount(orders.get(0).getOrderAmount());
        order.setOrderId(orders.get(0).getOrderId());
        order.setOrderName(orders.get(0).getOrderName());
        order.setPayAmount(paypalExecuteResVO.getPayAmount());
        orderList.add(order);
        paypalExecuteResVO.setOrders(orderList);*/

        // 针对该用户加锁，5秒后自动释放，并发等待5秒获取，5秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_ORDER_PAY, uid, 5, 5)) {
            BusinessErrorInfoEnum.EN_ORDER_PAY_TIMEOUT.assertIsTrue(lock.getLockRes());

            // 判断并返回缓存中的确认支付信息,缓存时长为半小时
            Optional<UnifiedOrderResVO>  payInfo = this.getCacheExePayInfo(uid, reqVO, orders);
            if (payInfo.isPresent()) {
                JSONObject jsonObject= JSON.parseObject(JsonUtil.toJsonString(payInfo));
                paypalExecuteResVO= jsonObject.toJavaObject(PaypalExecuteResVO.class);
                paypalExecuteResVO.setPayWay(reqVO.getPayWay());
                paypalExecuteResVO.setPayChannel(reqVO.getPayChannel());
                paypalExecuteResVO.setOrderName(orders.get(0).getOrderName());
                paypalExecuteResVO.setOrderAmount(orders.get(0).getOrderAmount());
                paypalExecuteResVO.setFlowTime( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
                return paypalExecuteResVO;
            }

            PaypalExecuteDTO paypalExecuteDTO=new PaypalExecuteDTO();
            paypalExecuteDTO.setPaymentId(paymentId); //支付中心支付订单号
            paypalExecuteDTO.setFlowId(orders.get(0).getFlowId());//支付流水号

            // 调用支付中心paypal确认支付服务
            Result<UnifiedOrderResVO> result = paymentFeignService.paypalExecute(paypalExecuteDTO);
            log.info("支付单号:{},支付返回信息:{}", paymentId, result);
            BusinessErrorInfoEnum.EN_ORDER_PAY_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0);

            // 缓存支付结果信息
            String cacheUniqueKey = orderIdHelper.getUniqueId(uid, reqVO.getOrderId(),
                    reqVO.getPayWay(), reqVO.getPayScene());
            log.info("支付订单，获取cacheUniqueKey：{}", cacheUniqueKey);
            OrderCache.prePayInfo(cacheUniqueKey, result.getData());

            // 缓存支付参数
            PaypalParamsCacheBO paypalParamsCacheBO = ObjectConvertUtil.convert(reqVO, PaypalParamsCacheBO.class).get();
            paypalParamsCacheBO.setUid(uid);
            OrderCache.paypalParams(paymentId, paypalParamsCacheBO);

            JSONObject jsonObject= JSON.parseObject(JsonUtil.toJsonString(result.getData()));
            paypalExecuteResVO= jsonObject.toJavaObject(PaypalExecuteResVO.class);
            paypalExecuteResVO.setPayWay(reqVO.getPayWay());
            paypalExecuteResVO.setPayChannel(reqVO.getPayChannel());
            paypalExecuteResVO.setOrderName(orders.get(0).getOrderName());
            paypalExecuteResVO.setOrderAmount(orders.get(0).getOrderAmount());
            paypalExecuteResVO.setFlowTime( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));


          /* 等待ipn回调通知支付结果，这里先不做状态更改
            //修改订单状态
            if("success".equals(paypalExecuteResVO.getPayStatus())){
                QueryWrapper<IntOrder> updateCondition = new QueryWrapper<>();
                updateCondition.eq("order_id", reqVO.getOrderId());

                IntOrder updateOrder = new IntOrder();
                updateOrder.setOrderStatus(OrderStatusEnum.paid.getCode());
                updateOrder.setPayStatus(OrderPayStatusEnum.paid.getCode());
                // 更新订单优惠金额、实际支付金额
                updateOrder.setPayAmount(paypalExecuteResVO.getPayAmount());
                intOrderMapper.update(updateOrder, updateCondition);
            }*/
            //如果支付中心返回的支付状态为success,将订单表支付状态修改为支付中。 等ipn回调后再修改为最终状态
            if("success".equals(paypalExecuteResVO.getPayStatus())){
                QueryWrapper<IntOrder> updateCondition = new QueryWrapper<>();
                updateCondition.eq("order_id", reqVO.getOrderId());
                updateCondition.eq("pay_status", OrderPayStatusEnum.un_pay.getCode());
                IntOrder updateOrder = new IntOrder();
                updateOrder.setPayStatus(OrderPayStatusEnum.paying.getCode());
                intOrderMapper.update(updateOrder, updateCondition);
            }

            return paypalExecuteResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.EN_ORDER_PAY_TIMEOUT, null, e.getMessage(), e);
        }
    }

    /**
     * 获取缓存中的确认支付信息
     * @param reqVO
     * @param orders
     * @return
     */
    private Optional<UnifiedOrderResVO> getCacheExePayInfo(String uid, PaypalExecuteReqVO reqVO, List<IntOrder> orders) {
        String cacheUniqueKey = orderIdHelper.getUniqueId(uid, orders.get(0).getOrderId(), reqVO.getPayWay(), reqVO.getPayScene());
        // 判断订单是否重复支付，直接从缓存拿确认支付信息
        Optional<UnifiedOrderResVO> exePayInfo = OrderCache.getPrePayInfo(cacheUniqueKey);
        if (exePayInfo.isPresent()) {
            return exePayInfo;
           // return Optional.of(String.valueOf(exePayInfo.get().get("pay_info")));
        }
        return Optional.empty();
    }

    /**
     * 退款
     * @param refundVO
     * @return
     * @throws BusinessException
     */
    @Override
    @Transactional
    public RefundCreateResVO refundOrder(RefundVO refundVO) throws BusinessException {

        IntOrder intOrder = get(refundVO.getUid(), refundVO.getOrderId());

        BusinessErrorInfoEnum.ORDER_REFUND_NO_DATA.assertNotNull(intOrder);
        BusinessErrorInfoEnum.ORDER_REFUND_STATUS_ERROR.assertIsTrue(OrderStatusEnum.paid.getCode().equals(intOrder.getOrderStatus()));

        // 针对该用户加锁，5秒后自动释放，并发等待5秒获取，5秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_ORDER_REFUND, refundVO.getOrderId(), 5, 5)) {
            BusinessErrorInfoEnum.ORDER_REFUND_TIMEOUT.assertIsTrue(lock.getLockRes());

            // 生成退款参数
            List<IntRefund> intRefunds =generateOrderRefunds(refundVO, intOrder);
            IntRefundDTO intRefundDTO=generatePaymentRefund(refundVO,intRefunds);

            // 调用支付中心退款
            log.info("退款单号:{},退款请求信息:{}", intRefundDTO.getPaymentId(), JsonUtil.toJsonString(intRefundDTO));
            Result result = paymentFeignService.refundIntOrder(intRefundDTO);
            log.info("退款单号:{},退款返回信息:{}", intRefundDTO.getPaymentId(), result);
            BusinessErrorInfoEnum.ORDER_REFUND_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0, StringUtils.isNotEmpty(result.getMsg())?result.getMsg():"");

            // 修改子订单状态退款中
            String[] subOrderIds = refundVO.getSubOrders().stream().map(RefundVO.SubOrder::getSubOrderId).toArray(String[]::new);
            intSubOrderService.refundIng(subOrderIds);

            Map<String, Object> resultMap = JsonUtil.fromJson(result.toString(), Map.class);
            Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");

            String flowId = String.valueOf(dataMap.get("flow_id"));//退款流水号
            Integer refundAmount = (Integer) dataMap.get("refund_amount");// 实际退款金额。这里是退款总金额。无法确定哪个子订单退了多少，

            intRefunds.forEach(intRefund -> {
                intRefund.setFlowId(flowId);
                //intRefund.setRefundStatus(refundStatus);
                //intRefund.setRefundTime(flowTime);
                //intRefund.setRefundAmount(refundAmount);//实际每个子订单的退款金额无法确定，这里先不做更新
            });

            // 保存退款记录
            intRefundMapper.saveBatch(intRefunds);
            RefundCreateResVO resVO = new RefundCreateResVO();
            resVO.setOrderId(intOrder.getOrderId());// 主订单号
            resVO.setPaymentId(intRefunds.get(0).getPaymentId());//退款单号
            intRefunds.forEach(refund -> resVO.getSubOrders().add(
                    new RefundCreateResVO.SubOrder(
                            refund.getSubOrderId(),
                            refund.getRefundId(),
                            refund.getSubOrderAmount(),
                            refund.getSubOrderPayAmount(),
                            refund.getRefundAmount())));
            return resVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_REFUND_TIMEOUT, null, e.getMessage(), e);
        }
    }

    /**
     * 生成退款单
     * @param refundVO
     * @param intOrder
     * @return
     */
    private List<IntRefund> generateOrderRefunds(RefundVO refundVO, IntOrder intOrder) {
        String[] subOrderIds = refundVO.getSubOrders().
                stream().map(RefundVO.SubOrder::getSubOrderId).toArray(String[]::new);
        List<IntSubOrder> intSubOrders = intSubOrderService.findCanRefund(refundVO.getOrderId(), subOrderIds);

        // 子订单新提交的退款
        Map<String, Integer> applyRefundMap = refundVO.getSubOrders().stream().collect(Collectors.toMap(
                RefundVO.SubOrder::getSubOrderId, x -> x.getRefundAmount() != null ? x.getRefundAmount() : 0));

        //获取退款支付单号
        String paymentId = orderIdHelper.getRefundPaymentId(intOrder.getOrderSource(), refundVO.getUid());

        List<IntRefund> refunds = new ArrayList<>();
        for (IntSubOrder intSubOrder : intSubOrders) {
            IntRefund intRefund = new IntRefund();
            intRefund.setUid(refundVO.getUid());
            //退款id
            intRefund.setRefundId(orderIdHelper.getRefund(intOrder.getOrderSource(), refundVO.getUid()));
            intRefund.setPaymentId(paymentId);//退款单号
            intRefund.setPayFlowId(intOrder.getFlowId());//支付流水号
            intRefund.setOrderId(intSubOrder.getOrderId());
            intRefund.setSubOrderId(intSubOrder.getSubOrderId());
            intRefund.setOrderSource(intSubOrder.getOrderSource());
            intRefund.setMerchantCode(intSubOrder.getMerchantCode());
            intRefund.setMerchantName(intSubOrder.getMerchantName());
            intRefund.setProductCode(intSubOrder.getProductCode());
            intRefund.setProductName(intSubOrder.getProductName());
            intRefund.setSystemSource(intOrder.getSystemSource());
            // 退款金额，如果未提交，默认使用子订单支付金额
            // 实际退款金额
            int refundAmount = applyRefundMap.get(intSubOrder.getSubOrderId()) != 0
                    ? applyRefundMap.get(intSubOrder.getSubOrderId()) : intSubOrder.getPayAmount();
            intRefund.setRefundApplyAmount(refundAmount);
            intRefund.setRefundAmount(refundAmount);
            intRefund.setSubOrderAmount(intSubOrder.getSubOrderAmount());
            intRefund.setSubOrderPayAmount(intSubOrder.getPayAmount());
            intRefund.setDiscount(intSubOrder.getDiscount());
            intRefund.setRefundStatus(RefundStatusEnum.ing.getCode());
            intRefund.setRefundReason(refundVO.getRefundReason());
            intRefund.setCreatedDt(refundVO.getCreateTime());
            intRefund.setCurrency(intOrder.getCurrency());
            intRefund.setSubOrderStatus(intSubOrder.getSubOrderStatus());
            refunds.add(intRefund);
        }
        return refunds;
    }


    /**
     * 生成支付中心退款参数
     * @param refundVO
     * @param intRefunds
     * @return
     */
    public IntRefundDTO generatePaymentRefund(RefundVO refundVO, List<IntRefund> intRefunds) {
        // 提交退款
        IntRefundDTO intRefundDTO = new IntRefundDTO();
        intRefundDTO.setUid(refundVO.getUid());
        intRefundDTO.setPaymentId(intRefunds.get(0).getPaymentId());//退款单号
        intRefundDTO.setPayFlowId(intRefunds.get(0).getPayFlowId());//支付流水号
        intRefundDTO.setCurrency(intRefunds.get(0).getCurrency());
        // 使用实退金额
        intRefundDTO.setAmount(intRefunds.stream().mapToInt(IntRefund::getRefundAmount).sum());
        intRefundDTO.setReason(refundVO.getRefundReason());
        intRefundDTO.setOrderAmount(intRefunds.stream().mapToInt(IntRefund::getSubOrderAmount).sum());
        intRefundDTO.setDiscount(intRefunds.stream().mapToInt(IntRefund::getDiscount).sum());
        intRefundDTO.setIntSubOrderRefundDTOS(new ArrayList<>());

        intRefunds.forEach(intRefund -> {
            IntRefundDTO.IntSubOrderRefundDTO intSubOrderRefundDTO = new IntRefundDTO.IntSubOrderRefundDTO();
            intSubOrderRefundDTO.setSubOrderId(intRefund.getSubOrderId());
            intSubOrderRefundDTO.setAmount(intRefund.getRefundAmount());
            intSubOrderRefundDTO.setOrderAmount(intRefund.getSubOrderAmount());
            intSubOrderRefundDTO.setDiscount(intRefund.getDiscount());
            intRefundDTO.getIntSubOrderRefundDTOS().add(intSubOrderRefundDTO);
        });
        return intRefundDTO;
    }



    @Override
    public OrderDetailResVO queryOrderDetailsByOrderId(String OrderId) throws BusinessException {
        OrderDetailResVO parametersVO = new OrderDetailResVO();
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("order_id", OrderId);
        IntOrder intOrder = intOrderMapper.selectOne(conditions);
        OrderDetailVO orderDetailVO = ObjectConvertUtil.convert(intOrder, OrderDetailVO.class).get();
        orderDetailVO.setPayExpiryTime(orderDetailVO.getPayExpiryTime());
        orderDetailVO.setPayChannelCN(PaymentChannelEnum.getDesc(intOrder.getPayChannel()));
        orderDetailVO.setPayWayCN(PaymentWayEnum.getDesc(intOrder.getPayWay()));
        List<IntSubOrder> subOrders = null;
        List<SubOrderDetailVO> subOrderDetailVO = null;
        List<RefundOrdersDetail> refundOrdersDetails = null;
        List<RefundDetailResVO> refundDetailResVOS = null;
        List<Coupon> coupons = null;
        List<CouponDetailVO> couponDetailVOS = null;
        if (null != intOrder) {
            subOrders = intSubOrderService.findByOrderId(intOrder.getOrderId());
            subOrderDetailVO = subOrders.stream().map(subOrder ->
                    ObjectConvertUtil.convert(subOrder, SubOrderDetailVO.class).get()).collect(Collectors.toList());
            subOrderDetailVO.forEach(subOrderDetailVO1 -> subOrderDetailVO1.setSubOrderStatusCN(SubOrderStatusEnum.getDesc(subOrderDetailVO1.getSubOrderStatus())));

            refundOrdersDetails = refundOrdersDetailService.getRefundList(intOrder.getOrderId());
            if (CollectionUtils.isNotEmpty(refundOrdersDetails)) {
                refundDetailResVOS = refundOrdersDetails.stream().map(refund ->
                        ObjectConvertUtil.convert(refund, RefundDetailResVO.class).get()).collect(Collectors.toList());
            }
            orderDetailVO.setCouponNum(coupons.size());
            orderDetailVO.setSettlementTags(intOrder.getSettlementTags());
            orderDetailVO.setOrderStatusCN(OrderStatusEnum.getDesc(orderDetailVO.getOrderStatus()));
            orderDetailVO.setPayStatusCN(OrderPayStatusEnum.getDesc(orderDetailVO.getPayStatus()));
            orderDetailVO.setOrderSourceCN(OrderSourceEnum.getDesc(orderDetailVO.getOrderSource()));
            orderDetailVO.setOrderTypeCN(OrderTypeEnum.getDesc(orderDetailVO.getOrderType()));
        }
        if (CollectionUtils.isNotEmpty(subOrderDetailVO)) {
            orderDetailVO.setMerchantCode(subOrderDetailVO.get(0).getMerchantCode());
            orderDetailVO.setMerchantName(subOrderDetailVO.get(0).getMerchantName());
        }
        if (CollectionUtils.isNotEmpty(coupons)) {
            couponDetailVOS = coupons.stream().map(coupon ->
                    ObjectConvertUtil.convert(coupon, CouponDetailVO.class).get()).collect(Collectors.toList());
            couponDetailVOS.stream().forEach(a -> {
                a.setIsCoupon(true);
                a.setCouponTypeCN(CouponTypeEnum.getDesc(a.getCouponType()));
                a.setDiscountSourceCN(OrderSourceEnum.getDesc(a.getDiscountSource()));
            });
            orderDetailVO.setCouponNum(coupons.size());
            orderDetailVO.setIsCoupon(true);
        }
        parametersVO.setOrderDetailVO(orderDetailVO);
        parametersVO.setSubOrders(subOrderDetailVO);
        parametersVO.setCoupons(couponDetailVOS);
        parametersVO.setRefunds(refundDetailResVOS);
        return parametersVO;
    }

    @Override
    public PageInfo<OrderResVO> queryOrderList(QueryOrdersVO orderVO) {
        QueryWrapper<IntOrder> query = buildQuery(orderVO);
        query.orderByDesc("created_dt");
        PageInfo<IntOrder> pageInfo = intOrderMapper.find(query, orderVO.getCurrentPage(), orderVO.getPageSize());
        PageInfo<OrderResVO> pageRes = new PageInfo<>();
        List<OrderResVO> vos = pageInfo.getList().stream().map(order ->
                ObjectConvertUtil.convert(order, OrderResVO.class).get()).collect(Collectors.toList());
        vos.forEach(orderResVO -> orderResVO.setOrderStatusCN(OrderStatusEnum.getDesc(orderResVO.getOrderStatus())));
        vos.forEach(orderResVO -> orderResVO.setPayStatusCN(OrderPayStatusEnum.getDesc(orderResVO.getPayStatus())));
        vos.forEach(orderResVO -> orderResVO.setOrderSource(OrderSourceEnum.getDesc(orderResVO.getOrderSource())));
        pageRes.setList(vos);
        pageRes.setPagination(pageInfo.getPagination());
        return pageRes;
    }

    private QueryWrapper<IntOrder> buildQuery(QueryOrdersVO orderVO) {
        QueryWrapper<IntOrder> query = new QueryWrapper<>();

        if (StrUtil.isNotBlank(orderVO.getOrderId())) {
            query.eq("order_id", orderVO.getOrderId());
        }
        if (StrUtil.isNotBlank(orderVO.getOriginalOrderId())) {
            query.eq("original_order_id", orderVO.getOriginalOrderId());
        }
        if (StrUtil.isNotBlank(orderVO.getPaymentId())) {
            query.eq("payment_id", orderVO.getPaymentId());
        }
        if (StrUtil.isNotBlank(orderVO.getFlowId())) {
            query.eq("flow_id", orderVO.getFlowId());
        }
        if (StrUtil.isNotBlank(orderVO.getOrderSource())) {
            query.eq("order_source", orderVO.getOrderSource());
        }
        if (StrUtil.isNotBlank(orderVO.getOrderStatus())) {
            query.eq("order_status", orderVO.getOrderStatus());
        }
        if (StrUtil.isNotBlank(orderVO.getPayStatus())) {
            query.eq("pay_status", orderVO.getPayStatus());
        }
        if (!StringUtils.isEmpty(orderVO.getCreateTimeBegin()) && !StringUtils.isEmpty(orderVO.getCreateTimeEnd())) {
            query.between("created_dt", DateTimeUtil.format(orderVO.getCreateTimeBegin() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"),
                    DateTimeUtil.format(orderVO.getCreateTimeEnd() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
        }

        if (!StringUtils.isEmpty(orderVO.getPayTimeBegin()) && !StringUtils.isEmpty(orderVO.getPayTimeEnd())) {
            query.between("pay_time",
                    DateTimeUtil.format(orderVO.getPayTimeBegin() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"),
                    DateTimeUtil.format(orderVO.getPayTimeEnd() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
        }

        return query;
    }

    /**
     * 保存paypal支付通知结果
     * @param intPayNotifyBO
     * @throws BusinessException
     */
    @Override
    @Transactional
    public void savePayResult(IntPayNotifyBO intPayNotifyBO) throws BusinessException {

        //paypal支付的订单流水已存入到订单表，可以根据回调的流水号查询对应的订单号，不需要从缓存取
        //根据流水号查询订单号
        String orderId=intOrderMapper.getOrderIdByFlowId(intPayNotifyBO.getFlowId());
        List<IntOrder> orders=new ArrayList<>();
        if(orderId!=null&&!"".equals(orderId)){
            //根据订单号获取订单信息
            QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
            conditions.in("order_id", orderId);
            orders= intOrderMapper.selectList(conditions);

        }else{
            //查不到订单再从缓存取
            Optional<PaypalParamsCacheBO> payParams = OrderCache.getPaypalParams(intPayNotifyBO.getPaymentId());
            BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_PAYMENT.assertIsTrue(payParams.isPresent());

            QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
            conditions.in("order_id", payParams.get().getOrderId());
            orders= intOrderMapper.selectList(conditions);
        }
        BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_DATA.assertIsTrue(orders.size() > 0);
        String payWay=orders.get(0).getPayWay();
        String payScene=orders.get(0).getPayScene();

        // 针对该用户加锁，5秒后自动释放，并发等待60秒获取，60秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_NOTIFY_PAY, orders.get(0).getUid(), 60, 5)) {
            BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_LOCK.assertIsTrue(lock.getLockRes());

            long validNum = orders.stream().filter(order -> !(OrderStatusEnum.waiting_pay.equals(order.getOrderStatus()) &&
                    (OrderPayStatusEnum.un_pay.equals(order.getPayStatus())
                            ||OrderPayStatusEnum.paying.equals(order.getPayStatus())))).count();
            // 检查订单状态是否正确
            if (validNum == orders.size()) {
                int discountTotal = intPayNotifyBO.getCouponAmount();
                log.info("修改订单优惠信息：discountTotal:{}",discountTotal);
                if(discountTotal>0){
                    int sumOrderAmount = orders.stream().mapToInt(IntOrder::getOrderAmount).sum();
                    for (int i = 0; i < orders.size(); i++) {
                        if (i == orders.size() - 1) {
                            orders.get(i).setDiscount(discountTotal - orders.stream().mapToInt(IntOrder::getDiscount).sum());
                        } else {
                            int divide = BigDecimal.valueOf(orders.get(i).getOrderAmount()).
                                    divide(BigDecimal.valueOf(sumOrderAmount), 10,BigDecimal.ROUND_DOWN)
                                    .multiply(BigDecimal.valueOf(discountTotal )).setScale(0, BigDecimal.ROUND_UP).intValue();
                            orders.get(i).setDiscount(divide);
                        }
                        orders.get(i).setIsCoupon(orders.get(i).getDiscount() > 0);
                        QueryWrapper<IntOrder> updateCondition = new QueryWrapper<>();
                        updateCondition.eq("uid", orders.get(i).getUid());
                        updateCondition.eq("order_id", orders.get(i).getOrderId());
                        updateCondition.eq("id", orders.get(i).getId());
                        IntOrder updateOrder = new IntOrder();
                        updateOrder.setPaymentId(intPayNotifyBO.getPaymentId());
                        updateOrder.setFlowId(intPayNotifyBO.getFlowId());
                        updateOrder.setPayTime(intPayNotifyBO.getPayTime());
                        updateOrder.setOrderStatus(OrderStatusEnum.paid.getCode());
                        updateOrder.setPayStatus(OrderPayStatusEnum.paid.getCode());
                        updateOrder.setPayWay(payWay);
                        updateOrder.setPayScene(payScene);
                        updateOrder.setBuyerUid(intPayNotifyBO.getUserId());
                        updateOrder.setDiscount(orders.get(i).getDiscount());
                        updateOrder.setIsCoupon(orders.get(i).getDiscount()>0);
                        // 更新订单优惠金额、实际支付金额
                        updateOrder.setPayAmount(orders.get(i).getPayAmount());
                        int num = intOrderMapper.update(updateOrder, updateCondition);
                        // 修改子订单已支付
                        log.info("修改子订单订单优惠信息：orders.get(i).getDiscount():{}",orders.get(i).getDiscount());
                        intSubOrderService.paid(orders.get(i).getOrderId(),orders.get(i).getDiscount());
                    }
                }else{
                    // 修改订单状态为支付成功
                    IntOrder updateOrder = new IntOrder();
                    updateOrder.setPaymentId(intPayNotifyBO.getPaymentId());
                    updateOrder.setFlowId(intPayNotifyBO.getFlowId());
                    updateOrder.setPayTime(intPayNotifyBO.getPayTime());
                    updateOrder.setOrderStatus(OrderStatusEnum.paid.getCode());
                    updateOrder.setPayStatus(OrderPayStatusEnum.paid.getCode());
                    updateOrder.setPayWay(payWay);
                    updateOrder.setPayScene(payScene);
                    updateOrder.setBuyerUid(intPayNotifyBO.getUserId());
                    QueryWrapper<IntOrder> updateCondition = new QueryWrapper<>();
                    updateCondition.in("order_id", orderId);
                    intOrderMapper.update(updateOrder, updateCondition);
                    // 修改子订单已支付
                    intSubOrderService.paid(orderId,0);
                }
            } else {
                // 如果状态不一致，说明是重复支付或者订单已取消，记录退款
                intPayNotifyBO.setRefund(true);
            }
            // 补全订单信息
            orders.forEach(order -> {
                order.setPayWay(payWay);
                order.setPayScene(payScene);
            });
            intPayNotifyBO.setOrders(orders);

            // 处理完支付回调，删除此笔支付参数缓存
            OrderCache.delPayParams(intPayNotifyBO.getPaymentId());
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_ERROR, null, e.getMessage(), e);
        }
    }


    @Override
    public IntOrder get(String orderId) {
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderId);
        return intOrderMapper.selectOne(conditions);
    }

    /***
     * 移动端查询订单数据
     * @param orderQueryVO
     * @return
     */
    @Override
    public PageInfo<IntOrderQueryResVO> findForApp(OrderQueryVO orderQueryVO) throws BusinessException {
        String uid = SecurityUtils.getLoginUserId();
        log.info("当前查询订单列表的用户id为：{}",uid);
      /*  根据时间来查询不合理，刚好卡在某一页最后一个订单相同的创建时间
        // 如果是下一页数据，就添加一个时间条件
        Optional<String> time = Optional.empty();
        if (orderQueryVO.getPage() > 1) {
            time = OrderCache.getQueryOrderNextTime(uid);
        }*/
        // 传order_source就查对应的，不传查所有
        String[] orderSources = null;
        if (!StringUtils.isEmpty(orderQueryVO.getOrderSource())) {
            orderSources = orderQueryVO.getOrderSource().split(",");
        }

        // 查询英文版用户订单数据
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        if (!StringUtils.isEmpty(orderQueryVO.getOrderStatus())) {
            conditions.eq("order_status", orderQueryVO.getOrderStatus());
        }
        /*if (time.isPresent()) {
            conditions.lt("created_dt", time.get());
        }*/
        if (orderSources != null && orderSources.length > 0) {
            conditions.in("order_source", orderSources);
        }
        // 不显示续单、尾单
        conditions.eq("original_order_id", "0");
        conditions.orderByDesc("created_dt");
        PageInfo<IntOrder> orders = intOrderMapper.find(conditions, orderQueryVO.getPage(), orderQueryVO.getSize());

        List<IntOrderQueryResVO> resOrders = new ArrayList<>();
        for (IntOrder order : orders.getList()) {
            OrderSourceEnum orderSourceEnum = EnumUtil.getEnumByCode(OrderSourceEnum.class, order.getOrderSource());
            IntOrderQueryResVO resVO = new IntOrderQueryResVO();
            resVO.setOrderId(order.getOrderId());
            resVO.setOrderStatus(order.getOrderStatus());
            resVO.setOrderName(order.getOrderName());
            resVO.setOrderSource(orderSourceEnum.name().toLowerCase());
            resVO.setOrderSourceName(orderSourceEnum.getDesc());
            resVO.setOrderStatusName(EnumUtil.getEnumByCode(OrderStatusEnum.class, resVO.getOrderStatus()).getDesc());
            //resVO.setPayAmount(new BigDecimal(order.getPayAmount()).divide(BigDecimal.valueOf(100),
            //        2, BigDecimal.ROUND_HALF_UP));//给app前端展示的金额以美元返回
            resVO.setPayAmount(order.getPayAmount());//给app前端展示的金额以美分返回
            resVO.setCurrency(order.getCurrency());
            resVO.setIsRefund(order.getRefundTimes() > 0 ? 1 : 0);
            resVO.setCreateTime(order.getCreatedDt());
            resVO.setBatchDesc(OrderParallelUtil.getBatchDesc(orderSourceEnum, order.getTradeInfo()));
            //resVO.setIsCanMergePay(false);
            JSONObject tradeInfoObj=JsonUtil.fromJson(order.getTradeInfo(),JSONObject.class);
            resVO.setTradeInfo(tradeInfoObj);
            resOrders.add(resVO);
        }

        /*// 数据排序，取指定条数
        resOrders = resOrders.stream().sorted(Comparator.comparing(IntOrderQueryResVO::getCreateTime)
                .reversed()).collect(Collectors.toList());
        resOrders = resOrders.subList(0, orderQueryVO.getSize() > resOrders.size()
                ? resOrders.size() : orderQueryVO.getSize());*/

        PageInfo<IntOrderQueryResVO> pageInfo = new PageInfo<>(resOrders,
                new PageInfo.Pagination(orderQueryVO.getPage(),
                orderQueryVO.getSize(),orders.getPagination().getTotal()));

       /* // 缓存下一页的查询时间点
        if (resOrders.size() > 0) {
            LocalDateTime nextTime = resOrders.get(resOrders.size() - 1).getCreateTime();
            OrderCache.queryOrderNextTime(uid, DateTimeUtil.format(nextTime, "yyyy-MM-dd HH:mm:ss"));
        }*/
        return pageInfo;
    }

    /**
     * 查询paypal支付信息
     * @param orderId
     * @return
     */
    @Override
    public Result<PaypalQueryResVO> getPaypalInfo(String orderId) {
        //根据订单号查询支付中心的支付订单号 paymentId
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderId);
        IntOrder intOrder=intOrderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.ORDER_NO_EXIST.assertNotNull(intOrder);

        String paymentId=intOrder.getPaymentId();
        BusinessErrorInfoEnum.ORDER_NO_PAY.assertNotEmpty(paymentId); // 订单未进行支付，不再查询

        //调用支付中心查询paypal支付信息
        Result result = paymentFeignService.getPaypalInfo(paymentId);
        log.info("查询支付单单号:{},paypal支付信息:{}", paymentId, JsonUtil.toJsonString(result));
        if(result.getRet() != 0){
            Result.fail(result.getMsg()) ;
        }
        Map<String, Object> resultMap = JsonUtil.fromJson(result.toString(), Map.class);
        Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");
        JSONObject jsonObject= JSON.parseObject(JsonUtil.toJsonString(dataMap));
        PaypalQueryResVO paypalQueryResVO=new PaypalQueryResVO();
        paypalQueryResVO= jsonObject.toJavaObject(PaypalQueryResVO.class);
        return Result.success(paypalQueryResVO);
    }


    @Override
    public List<IntOrder> find(String uid, String[] orderIds) {
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        conditions.in("order_id", orderIds);
        return intOrderMapper.selectList(conditions);
    }

    @Override
    public IntOrder get(String uid, String orderId) {
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        conditions.eq("order_id", orderId);
        return intOrderMapper.selectOne(conditions);
    }

    @Override
    @Transactional
    public int refund(boolean success, String orderId, List<SubOrderRefundBO> subOrderRefundBOS) {
        if (success) {
            intOrderMapper.refund(orderId, subOrderRefundBOS.size());
        }
        return intSubOrderService.refund(subOrderRefundBOS);
    }




    @Override
    public List<IntOrderJoin> findWithSubByFlowId(String flowId) {
        List<IntOrderJoin> list = intOrderMapper.findWithSubByFlowId(flowId);
        if (CollectionUtils.isNotEmpty(list)) {
            for (IntOrderJoin order : list) {
                List<IntSubOrder> subOrderList = order.getIntSubOrders();
                subOrderList.forEach(a -> {
                    a.setCouponType(CouponTypeEnum.getDesc(a.getCouponType()));
                });
            }
        }

        return list;
    }

    /**
     * 根据订单号查询主订单及子订单数据
     * @param orderId
     * @return
     */
    @Override
    public IntOrderJoin findByOrderId(String orderId) {
        List<IntOrderJoin> list = intOrderMapper.findWithSubByOrderId(orderId);
        if (CollectionUtils.isNotEmpty(list)) {
            for (IntOrderJoin order : list) {
                List<IntSubOrder> subOrderList = order.getIntSubOrders();
                subOrderList.forEach(a -> {
                    a.setCouponType(CouponTypeEnum.getDesc(a.getCouponType()));
                });
            }
            return list.get(0);
        }
        return null;
    }

    @Override
    public String getPayStatus(String uid, String orderId) throws BusinessException {
        IntOrder order = this.get(uid, orderId);
        BusinessErrorInfoEnum.EN_ORDER_NO_EXIST.assertNotNull(order);
        //当前订单的支付状态如果为支付中，则主动调用paypal查询接口进行查询
        if(OrderPayStatusEnum.paying.getCode().equals(order.getPayStatus())){
            log.info("订单号:{}, 当前支付状态为支付中，需要主动调用paypal查询接口查询",orderId);
            Result<PaypalQueryResVO> queryRes=this.getPaypalInfo(orderId);
            if(queryRes.getRet()!=0){
                return order.getPayStatus(); //支付中心查询失败直接返回订单表的状态
            }
            PaypalQueryResVO paypalQueryResVO=queryRes.getData();
            if("COMPLETED".equals(paypalQueryResVO.getStatus())){ //支付成功
                //再次查询订单信息
                IntOrder neworder = this.get(uid, orderId);
                if(OrderPayStatusEnum.paying.getCode().equals(neworder.getPayStatus())){
                    IntPayNotifyBO intPayNotifyBO=new IntPayNotifyBO();
                    intPayNotifyBO = JsonUtil.fromJson(paypalQueryResVO.getPayInfo(), IntPayNotifyBO.class);
                    log.info("保存的信息为:{}",intPayNotifyBO);
                    this.intPayResult(intPayNotifyBO);
                }
                return OrderPayStatusEnum.paid.getCode();
            }
        }
        return order.getPayStatus();
    }


    @Override
    public List<IntOrder> findByOrderIds(Set<String> orderIds) {
        QueryWrapper<IntOrder> conditions = new QueryWrapper<>();
        conditions.in("order_id", orderIds);
        return intOrderMapper.selectList(conditions);
    }


    /**
     * 英文版app初始化收银台信息查询
     * @param uid
     * @param reqVO
     * @return
     * @throws BusinessException
     */
    @Override
    public IntCashierResVO initCashier(String uid, IntCashierReqVO reqVO) throws BusinessException {

        QueryWrapper conditions = new QueryWrapper();
        conditions.eq("uid", uid);
        conditions.eq("order_id", reqVO.getOrderId());
        conditions.eq("order_status", OrderStatusEnum.waiting_pay.getCode());
        List<IntOrder> orders = intOrderMapper.selectList(conditions);
        BusinessErrorInfoEnum.EN_ORDER_CASHIER_STATUS.assertIsTrue(orders.size() ==1);

        // 获取订单的支付渠道，并判断禁止跨渠道支付，以及判断订单是否支付超时
        String payChannel = orders.get(0).getPayChannel();
        for (IntOrder order : orders) {
            BusinessErrorInfoEnum.EN_ORDER_CASHIER_CHANNEL_DIFF.assertIsTrue(payChannel.equals(order.getPayChannel()));
            BusinessErrorInfoEnum.EN_ORDER_PAY_TIMEOUT_CLOSE.assertIsTrue(
                    order.getPayExpiryTime().isAfter(LocalDateTime.now()));
        }

        // 判断优惠券，目前只允许带一张优惠券支付，如果是合单支付，不允许带优惠券
       // List<Coupon> coupons = couponService.find(cashierVO.getOrderIds());
       // BusinessErrorInfoEnum.ORDER_CASHIER_COUPON_ONE.assertIsTrue(coupons.size() <= 1);
        //BusinessErrorInfoEnum.ORDER_CASHIER_MERGE_COUPON.assertIsTrue(orders.size() == 1 || coupons.size() == 0);

        IntCashierResVO intCashierResVO = new IntCashierResVO();
        intCashierResVO.setOrderSource(orders.size() > 1 ? OrderSourceEnum.CONSOLIDATION.getCode() : orders.get(0).getOrderSource());
        intCashierResVO.setType(EnumUtil.getEnumByCode(OrderSourceEnum.class, intCashierResVO.getOrderSource()).name().toLowerCase());
        intCashierResVO.setOrderName(orders.get(0).getOrderName());

        LocalDateTime payExpiryTime = orders.stream().map(IntOrder::getPayExpiryTime).min(
                Comparator.comparing(date -> date.toEpochSecond(ZoneOffset.of("+8")))).get();
        intCashierResVO.setPayLeftTime(payExpiryTime.toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
        intCashierResVO.setPayAmount(orders.get(0).getPayAmount());
        intCashierResVO.setOrderAmount(orders.get(0).getOrderAmount());
        intCashierResVO.setCurrency(orders.get(0).getCurrency());
       //支付方式目前只有paypal
        intCashierResVO.setPayWay((orders.get(0).getPayWay()==null||"".equals(orders.get(0).getPayWay()))
                ?PaymentWayEnum.PAYPAL.getCode():orders.get(0).getPayWay());
        intCashierResVO.setPayChannel(payChannel);

        return intCashierResVO;
    }


    /**
     * 处理paypal支付结果
     * @param intPayNotifyBO
     * @throws BusinessException
     */
    @Override
    public void intPayResult(IntPayNotifyBO intPayNotifyBO) throws BusinessException {
        this.savePayResult(intPayNotifyBO);
        log.info("开始推送支付结果消息:{}", JsonUtil.toJsonString(intPayNotifyBO));
        String uid=intPayNotifyBO.getUserId();

        // 通知业务侧支付成功
        List<IntOrder> orders = this.findByOrderIds(intPayNotifyBO.getOrders().stream().
                map(IntOrder::getOrderId).collect(Collectors.toSet()));
        intPayNotifyBO.setOrders(orders);
//        for (IntOrder intOrder : orders) {
//            PayResultMsgBO payResultMsgBO = new PayResultMsgBO();
//            payResultMsgBO.setOrderId(intOrder.getOrderId());
//            payResultMsgBO.setAmount(intOrder.getPayAmount());
//            payResultMsgBO.setOrderSource(intOrder.getOrderSource());
//            payResultMsgBO.setUid(intOrder.getUid());
//            payResultMsgBO.setStatus(intPayNotifyBO.getStatus());
//            payResultMsgBO.setPayTime(intPayNotifyBO.getPayTime());
//            payResultMsgBO.setChannelOrderId(intPayNotifyBO.getTradeNo());
//            payResultMsgBO.setFlowId(intPayNotifyBO.getFlowId());
//            payResultMsgBO.setPayChannel(intOrder.getPayWay());
//            payResultMsgBO.setPayScene(intOrder.getPayScene());
//            payResultMsgBO.setCouponAmount(intOrder.getDiscount());
//            messageProducer.payResult(payResultMsgBO);
//        }
    }
}
